﻿namespace Common
{
    using Microsoft.DirectX;
    using Microsoft.DirectX.Direct3D;
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;

    public class MeshObject
    {
        public string[] extraMeshDirs = new string[0];
        private Mesh _mesh;
        public string meshDir = "";
        protected Material[] _meshMaterials;
        protected string[] _meshTextureFilenames;
        protected Texture[] _meshTextures;
        private static ArrayList _meshFileStructs = new ArrayList();
        private static List<RenderStruct> _renderList = new List<RenderStruct>();
        private static ArrayList _textureFileStructs = new ArrayList();

        public virtual void CreateMesh()
        {
            if (this is IFromFile)
            {
                var filename = ((IFromFile)this).Filename;
                filename = meshDir + filename;
                var flag = false;
                foreach (MeshFileStruct struct2 in _meshFileStructs)
                {
                    if (struct2.filename != filename) continue;
                    _mesh = struct2.mesh;
                    _meshMaterials = (Material[])struct2.materials.Clone();
                    _meshTextures = (Texture[])struct2.textures.Clone();
                    _meshTextureFilenames = (string[])struct2.textureFilenames.Clone();
                    flag = true;
                }
                if (!flag)
                {
                    ExtendedMaterial[] materialArray;
                    //System.Windows.Forms.MessageBox.Show(Directory.GetCurrentDirectory() + filename);
                    try
                    {
                        _mesh = Mesh.FromFile(filename, MeshFlags.SystemMemory, MyDirect3D.device, out materialArray);
                        _meshMaterials = new Material[materialArray.Length];
                        _meshTextures = new Texture[materialArray.Length];
                        _meshTextureFilenames = new string[materialArray.Length];
                        for (var i = 0; i < materialArray.Length; i++)
                        {
                            _meshMaterials[i] = materialArray[i].Material3D;
                            if (string.IsNullOrEmpty(materialArray[i].TextureFilename)) continue;
                            _meshTextureFilenames[i] = meshDir + materialArray[i].TextureFilename;
                            LoadTexture(i, _meshTextureFilenames[i]);
                        }
                        var struct3 = new MeshFileStruct
                        {
                            filename = filename,
                            mesh = _mesh,
                            materials = (Material[])_meshMaterials.Clone(),
                            textures = (Texture[])_meshTextures.Clone(),
                            textureFilenames = (string[])_meshTextureFilenames.Clone()
                        };
                        _meshFileStructs.Add(struct3);
                    }
                    catch (Exception e)
                    {
                        MessageBox.Show(e.Source);
                    }
                }
            }
            else
            {
                if (!(this is ICustomCreation))
                {
                    //throw new Exception("Internal Error. The mesh object does not have information about building it's mesh.");
                    throw;
                }
                ((ICustomCreation)this).CreateCustomMesh();
            }
        }

        protected void LoadTexture(int index, string filename)
        {
            foreach (var str in extraMeshDirs)
            {
                if (!File.Exists(str + filename)) continue;
                filename = str + filename;
                break;
            }
            if (!File.Exists(filename))
            {
                //throw new FileNotFoundException("Texture file not found!", filename);
                throw;
            }
            var flag = false;
            foreach (TextureFileStruct struct2 in _textureFileStructs)
            {
                if (struct2.filename != filename) continue;
                _meshTextures[index] = struct2.texture;
                flag = true;
            }
            if (flag) return;
            _meshTextures[index] = TextureLoader.FromFile(MyDirect3D.device, filename);
            var struct3 = new TextureFileStruct { filename = filename, texture = _meshTextures[index] };
            _textureFileStructs.Add(struct3);
        }

        public void Render()
        {
            try
            {
                var flag = false;
                for (var i = 0; i < _meshMaterials.Length; i++)
                {
                    if ((_meshMaterials[i].Diffuse.A < 0xff) != MyDirect3D.Alpha) continue;
                    flag = true;
                    break;
                }
                if (flag)
                {
                    var num2 = ((IMatrixObject)this).MatricesCount;
                    for (var j = 0; j < num2; j++)
                    {
                        var matrix = ((IMatrixObject)this).GetMatrix(j);
                        if (matrix == Matrix.Zero) continue;
                        for (var k = 0; k < _meshMaterials.Length; k++)
                        {
                            if ((_meshMaterials[k].Diffuse.A < 0xff) == MyDirect3D.Alpha)
                            {
                                _renderList.Add(new RenderStruct(_meshMaterials[k], _meshTextures[k], _mesh, k, matrix));
                            }
                        }
                    }
                }
            }
            catch
            {
            }
        }

        public static void RenderList()
        {
            try
            {
                _renderList.Sort();
            }
            catch (Exception e)
            {
                System.Windows.Forms.MessageBox.Show(e.Message);
            }
            var structArray = _renderList.ToArray();
            for (var i = 0; i < structArray.Length; i++)
            {
                MyDirect3D.device.Material = structArray[i].material;
                MyDirect3D.device.SetTexture(0, structArray[i].texture);
                MyDirect3D.device.Transform.World = structArray[i].matrix;
                structArray[i].mesh.DrawSubset(structArray[i].subset);
            }
            _renderList.Clear();
        }

        private interface ICustomCreation : IMatrixObject
        {
            void CreateCustomMesh();
        }

        public interface IFromFile : IMatrixObject
        {
            string Filename { get; }
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct MeshFileStruct
        {
            public string filename;
            public Mesh mesh;
            public Material[] materials;
            public Texture[] textures;
            public string[] textureFilenames;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct RenderStruct : IComparable
        {
            public Material material;
            public Texture texture;
            public Mesh mesh;
            public int subset;
            public Matrix matrix;
            public RenderStruct(Material material, Texture texture, Mesh mesh, int subset, Matrix matrix)
            {
                this.material = material;
                this.texture = texture;
                this.mesh = mesh;
                this.subset = subset;
                this.matrix = matrix;
            }

            public int CompareTo(object obj)
            {
                if (!(obj is RenderStruct))
                {
                    //throw new ArgumentException();
                    throw;
                }
                var struct2 = this;
                var struct3 = (RenderStruct)obj;
                if (struct2.texture != struct3.texture)
                {
                    var num = (struct2.texture != null) ? struct2.texture.GetHashCode() : 0;
                    var num2 = (struct3.texture != null) ? struct3.texture.GetHashCode() : 0;
                    if (num != num2)
                    {
                        return (num - num2);
                    }
                    return -1;
                }
                if (struct2.material != struct3.material)
                {
                    if (struct2.material.AmbientColor.ToArgb() != struct3.material.AmbientColor.ToArgb())
                    {
                        return (struct2.material.AmbientColor.ToArgb() - struct3.material.AmbientColor.ToArgb());
                    }
                    if (struct2.material.DiffuseColor.ToArgb() != struct3.material.DiffuseColor.ToArgb())
                    {
                        return (struct2.material.DiffuseColor.ToArgb() - struct3.material.DiffuseColor.ToArgb());
                    }
                    if (struct2.material.EmissiveColor.ToArgb() != struct3.material.EmissiveColor.ToArgb())
                    {
                        return (struct2.material.EmissiveColor.ToArgb() - struct3.material.EmissiveColor.ToArgb());
                    }
                    if (struct2.material.SpecularColor.ToArgb() != struct3.material.SpecularColor.ToArgb())
                    {
                        return (struct2.material.SpecularColor.ToArgb() - struct3.material.SpecularColor.ToArgb());
                    }
                    return Math.Sign(struct2.material.SpecularSharpness - struct3.material.SpecularSharpness);
                }
                if (!(struct2.matrix != struct3.matrix))
                {
                    return 0;
                }
                if (struct2.matrix.Determinant != struct3.matrix.Determinant)
                {
                    return Math.Sign(struct2.matrix.Determinant - struct3.matrix.Determinant);
                }
                return -1;
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct TextureFileStruct
        {
            public string filename;
            public Texture texture;
        }
    }
}

